package com.geneticAlgorithm.wines;

public class GA {
	/* GA parameters */
    private static final double mutationRate = 0.015;
    private static final int tournamentSize = 5;
    private static final boolean elitism = true;

    // Evolves a population over one generation
    public static Population evolvePopulation(Population pop) {
        Population newPopulation = new Population(pop.populationSize(), false);

        // Keep our best individual if elitism is enabled
        int elitismOffset = 0;
        if (elitism) {
            newPopulation.saveCandidate(0, pop.getFittest());
            elitismOffset = 1;
        }

        // Crossover population
        // Loop over the new population's size and create individuals from
        // Current population
        for (int i = elitismOffset; i < newPopulation.populationSize(); i++) {
            // Select parents
            CandidatesWines parent1 = tournamentSelection(pop);
            CandidatesWines parent2 = tournamentSelection(pop);
            // Crossover parents
            CandidatesWines child = crossover(parent1, parent2);
            // Add child to new population
            newPopulation.saveCandidate(i, child);
        }

        // Mutate the new population a bit to add some new genetic material
        for (int i = elitismOffset; i < newPopulation.populationSize(); i++) {
            mutate(newPopulation.getCandidate(i));
        }

        return newPopulation;
    }

    // Applies crossover to a set of parents and creates offspring
    public static CandidatesWines crossover(CandidatesWines parent1, CandidatesWines parent2) {
       
    	CandidatesWines child = new CandidatesWines();

        int startPos = (int) (Math.random() * parent1.candidatesSize());
        int endPos = (int) (Math.random() * parent1.candidatesSize());

        
        for (int i = 0; i < child.candidatesSize(); i++) {
           
            if (startPos < endPos && i > startPos && i < endPos) {
                child.setWine(i, parent1.getWine(i));
            } 
            else if (startPos > endPos) {
                if (!(i < startPos && i > endPos)) {
                    child.setWine(i, parent1.getWine(i));
                }
            }
        }

        
        for (int i = 0; i < parent2.candidatesSize(); i++) {
           
            if (!child.containsWine(parent2.getWine(i))) {
                
                for (int ii = 0; ii < child.candidatesSize(); ii++) {
                    
                    if (child.getWine(ii) == null) {
                        child.setWine(ii, parent2.getWine(i));
                        break;
                    }
                }
            }
        }
        return child;
    }

 
    private static void mutate(CandidatesWines candidatesWine) {
      
        for(int candidateWinePos1=0; candidateWinePos1 < candidatesWine.candidatesSize(); candidateWinePos1++){
           
            if(Math.random() < mutationRate){
                
                int candidateWinePos2 = (int) (candidatesWine.candidatesSize() * Math.random());

                
                Wine wine1 = candidatesWine.getWine(candidateWinePos1);
                Wine wine2 = candidatesWine.getWine(candidateWinePos2);

         
                candidatesWine.setWine(candidateWinePos2, wine1);
                candidatesWine.setWine(candidateWinePos1, wine2);
            }
        }
    }

   
    private static CandidatesWines tournamentSelection(Population pop) {
        
        Population tournament = new Population(tournamentSize, false);
       
        for (int i = 0; i < tournamentSize; i++) {
            int randomId = (int) (Math.random() * pop.populationSize());
            tournament.saveCandidate(i, pop.getCandidate(randomId));
        }
       
        CandidatesWines fittest = tournament.getFittest();
        return fittest;
    }
}
